package com.miya.classloaddemo;

import android.content.Context;
import android.util.Log;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;

/**
 * 加载dex文件
 */
public class LoadUtils {

    /**
     * 思路：
     * 1. 反射出BaseDexClassloader中的DexPathList,和其中的Element[]
     * 2. 使用宿主的PathClassLoader获取上两者的对象
     * 3. 创建插件的DexClassLoader
     * 4. 使用1中的field，反射出dexClassLoader的dex数组
     * 5. 合并两个数组，送给宿主的classloader
     */
    public static void loadDex(Context context) {
        if (isDexExists(context)) {
            Log.e("LoadUtils", "dexExists:" + getDexPath(context));
        } else {
            Log.e("LoadUtils", "dexNotExists:" + getDexPath(context));
        }
        //1. 反射出BaseClassLoader
        ClassLoader hostClassLoader = context.getClassLoader();
        if (!(hostClassLoader instanceof PathClassLoader)) {
            return;
        }
        try {
            PathClassLoader pathClassLoader = (PathClassLoader) hostClassLoader;

            //从BaseDexClassLoader的类中获取pathList的field
            Class<?> baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
            Field pathListField = baseDexClassLoaderClass.getDeclaredField("pathList");
            pathListField.setAccessible(true);
            //从dexPathList获取dex的数组
            Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList");
            //从pathList对应的类中获取elements的field
            Field dexElementsField = dexPathListClass.getDeclaredField("dexElements");
            dexElementsField.setAccessible(true);

            //从宿主的classloader获取pathList的对象
            Object hostPathList = pathListField.get(pathClassLoader);//将其所属的类的对象传进去，获取该field的对象
            if (hostPathList == null) {
                return;
            }
            //从DexPathList中获取dexElements对象
            Object[] hostDexArray = (Object[]) dexElementsField.get(hostPathList);
            if (hostDexArray.length <= 0) {
                return;
            }

            //插件使用的dexClassLoader
            DexClassLoader dexClassLoader = new DexClassLoader(getDexPath(context).getAbsolutePath(), context.getCacheDir().getAbsolutePath(), null, pathClassLoader);
            Log.e("LoadUtils", dexClassLoader.toString());
            Object dexPathList = pathListField.get(dexClassLoader);
            Object[] dexElementsArray = (Object[]) dexElementsField.get(dexPathList);
            if (dexElementsArray == null) {
                return;
            }

            logDexDetails(hostDexArray);
            logDexDetails(dexElementsArray);
            //问题：如何作出一个array，和elements一样的类型,答案：array.newInstance
            Object[] destArray = (Object[]) Array.newInstance(hostDexArray[0].getClass(), dexElementsArray.length + hostDexArray.length);//用已经反射出来的内容来创建新的数组
            System.arraycopy(hostDexArray, 0, destArray, 0, hostDexArray.length);
            System.arraycopy(dexElementsArray, 0, destArray, hostDexArray.length, dexElementsArray.length);

            logDexDetails(destArray);
            //将新的数组设置在宿主的dex数组中
            dexElementsField.set(hostPathList, destArray);

            Object[] newDexArray = (Object[]) dexElementsField.get(hostPathList);
            logDexDetails(newDexArray);
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | ClassCastException e) {
            e.printStackTrace();
        }
    }


    public static File getDexPath(Context context) {
        return new File(context.getCacheDir().getAbsolutePath(), "plugin1-release.apk");
    }

    public static boolean isDexExists(Context context) {
        return getDexPath(context).exists();
    }


    public static void logDexDetails(Object[] dexArray) {
        StringBuilder s = new StringBuilder();
        for (Object obj :
                dexArray) {
            s.append(obj.toString()).append("\n");
        }
        Log.e("LoadUtils", s.toString());
    }
}
